Skip to main content

02 - 三条隔离路线

前置01 - 为什么需要沙箱的四个隔离维度。

本篇回答:三种隔离机制分别在哪一层划边界、各自的开销来自哪里、以及它们防不住什么。

本篇会用到的词

意思
namespaceLinux 内核提供的隔离机制,让进程「看不见」别的进程、别的网络、别的文件系统。容器隔离的一半靠它
cgroupLinux 内核的资源限额机制,管 CPU、内存、IO 用多少。容器隔离的另一半靠它
KVMLinux 内核自带的虚拟化模块,借助 CPU 的硬件虚拟化指令划出一条真正的硬件边界。microVM 依赖它
嵌套虚拟化在一台已经是虚拟机的云主机里再跑虚拟机。很多云厂商默认不开,这直接决定了你能不能用 microVM
用户态内核gVisor 的做法:不用真内核,而是在普通用户进程里把内核接口重新实现一遍,把系统调用拦在进入宿主内核之前
runscgVisor 提供的容器运行时,符合 OCI 标准,所以可以直接替换掉 Docker 默认的 runc,镜像和编排都不用改
攻击面攻击者能够接触到的接口总量。共享内核意味着整个内核的系统调用都是攻击面,这是容器最主要的弱点

一、边界划在哪一层

三条路线的根本差异是攻击者要突破的那道墙由谁实现

gVisor 官方文档用三张图说明了这个差异,先看传统容器:

Linux 安全原语下的隔离

图片来源:google/gvisor g3doc/architecture_guide/,Apache-2.0

容器里的应用直接调用宿主内核。namespace 负责让它看不见别的进程和挂载点,cgroup 负责限制它用多少资源,seccomp 负责削减它能调用的系统调用集合。但系统调用最终仍然进入同一个内核 —— 内核里的一个提权漏洞就等于逃逸

虚拟化的做法是给它一个自己的内核:

虚拟化隔离

应用调用的是 Guest 内核,Guest 内核再通过硬件虚拟化边界(KVM)与宿主交互。攻击者要先攻破 Guest 内核,再攻破 VMM,才能碰到宿主。

gVisor 走的是第三条路:

gVisor 的隔离方式

它在用户态用 Go 重新实现了一套 Linux 系统调用接口。应用的系统调用被拦截后交给这个用户态内核处理,大部分调用根本不会到达宿主内核

二、容器:最快,但边界最薄

2.1 隔离靠三样东西拼出来

机制负责什么绕过后果
namespace让容器看不见宿主的进程、挂载、网络、用户能看见宿主全貌
cgroup限制 CPU、内存、IO 用量可打满宿主资源
seccomp / AppArmor削减可调用的系统调用集合可调用高危系统调用

这三样都是宿主内核提供的功能。也就是说,隔离的实现者和被隔离的对象共用同一个内核 —— 这是容器隔离的结构性弱点。

2.2 Agent 场景下的具体风险

模型生成的代码是不可信输入,它可能:

# 挂载宿主文件系统 —— 若容器有 CAP_SYS_ADMIN 或特权模式
mount /dev/sda1 /mnt

# 访问 Docker socket —— 若被误挂载进容器,等于拿到宿主 root
docker -H unix:///var/run/docker.sock run -v /:/host alpine chroot /host

# 直接利用已知内核提权漏洞

前两条是配置问题,可以通过不给特权、不挂 socket 来避免。第三条无法通过配置规避 —— 只要共享内核,内核漏洞就是共享风险。

结论:容器适合隔离「自己写的、只是可能有 bug 的代码」,不适合隔离「攻击者可以控制内容的代码」。Agent 执行模型生成的代码属于后者。

三、microVM:独立内核,启动仍然很快

传统虚拟机启动要几十秒,因为它模拟了一整套 PC 硬件。Firecracker 的做法是把设备模型砍到最小。

Firecracker 与宿主的集成方式

图片来源:firecracker-microvm/firecracker docs/images/,Apache-2.0

3.1 它砍掉了什么

传统 VM 有Firecracker后果
BIOS / UEFI 固件跳过固件引导阶段
PCI 总线、USB、显卡、声卡设备枚举耗时归零
完整的设备模型只有 virtio-net、virtio-block、串口、键盘控制器攻击面大幅缩小

官方给出的指标是启动约 125 毫秒、每个 microVM 内存开销约 5 MB。AWS Lambda 和 Fargate 跑在这套东西上,这是它最有说服力的生产背书。

3.2 代价

需要 KVM。 也就是需要硬件虚拟化支持。这带来一个实际约束:

  • 裸金属或支持嵌套虚拟化的云主机 —— 可以跑
  • 普通云厂商的虚拟机实例 —— 多数不支持嵌套虚拟化,跑不了

这是自建 microVM 沙箱最先撞上的墙,也是很多团队最终选择托管沙箱服务的直接原因。

四、gVisor:拦截系统调用

4.1 两个组件

Sentry 与 Gofer

图片来源:google/gvisor g3doc/,Apache-2.0

组件职责
Sentry用户态内核。拦截应用的系统调用并自行实现,只在必要时向宿主内核发起受限的调用
Gofer文件系统代理。Sentry 自己不直接访问宿主文件系统,所有文件操作经由 Gofer

拆成两个进程是有意为之:即使 Sentry 被攻破,它也没有直接的文件系统访问能力,还要再突破 Gofer。

4.2 平台:拦截方式可选

gVisor 的平台抽象

Sentry 怎么拦到系统调用,由「平台」这层抽象决定。这是理解 gVisor 性能特征的关键 —— 拦截方式不同,系统调用的额外开销差一个量级

4.3 它的代价是兼容性

gVisor 需要重新实现 Linux 系统调用。实现得再多也不可能是 100%:

  • 冷门系统调用可能未实现
  • 已实现的行为可能与宿主内核有细微差异
  • 直接操作硬件、依赖特定内核模块的程序跑不了

对 Agent 场景的实际影响:跑 Python、Node、常规命令行工具没问题;跑需要特殊内核特性的东西(某些 GPU 工作负载、eBPF 工具)会失败。

五、三条路线对照

容器microVMgVisor
内核共享宿主内核独立 Guest 内核用户态重新实现
隔离边界namespace / cgroup / seccomp硬件虚拟化(KVM)系统调用拦截
启动~100 ms~125 ms快于传统 VM
内存开销最小~5 MB / 实例中等
前置要求需要 KVM / 嵌套虚拟化
兼容性100%100%非 100%
内核漏洞影响直接逃逸需连续突破两层大部分调用不进宿主内核

5.1 组合使用是常态

三者不互斥。生产上常见的组合:

三者不互斥 —— 生产上常见的两种叠法组合一 · 换掉运行时gVisor作为容器运行时 runsc容器镜像与编排沿用 Docker 生态组合二 · 套两层microVM外层强隔离容器内层跑用户代码两种组合的共同点是保住了容器生态:镜像怎么构建、怎么分发、怎么编排都不用改,被换掉的只是最底下那一层。
这也是为什么「选哪条路线」在实践中往往不是二选一 —— 更常见的是先确定用容器生态,再决定它底下垫哪一层。
  • gVisor + 容器:gVisor 提供 runsc 运行时,可以直接替换 Docker 的默认运行时,镜像和编排都不用改
  • microVM + 容器:在 microVM 里跑容器运行时,兼顾强隔离与镜像生态。这是多数托管沙箱服务的实际架构

六、隔离防不住什么

三条路线都是执行隔离,它们的共同盲区是:

威胁沙箱能防吗
恶意代码删除宿主文件
恶意代码提权到宿主✅(强度不同)
占满 CPU / 内存✅ 靠 cgroup 或 VM 配额
把沙箱内读到的数据发到外网沙箱管不了
提示注入导致 Agent 主动做坏事❌ 沙箱看不出意图

倒数两行是重点。Agent 安全 · 防线在哪一层已经指出:沙箱是纵深防御的一环,数据外泄要靠网络出口管控,意图层面的攻击要靠工具级授权

一个只做了沙箱、没做出口管控的 Agent 平台,仍然会在提示注入攻击下把数据交出去 —— 因为那段代码在沙箱里跑得完全合法。

下一篇03 - E2B 与快照机制

← 回到 专题索引  ·  Agent Infra 板块总览